import express from 'express'
import request from 'request'
import * as cheerio from 'cheerio'
import iconv from 'iconv-lite'
import fs from 'fs'
import path from 'path'
import * as XLSX from 'xlsx'
import {fileURLToPath} from 'url'
import ExcelJS from 'exceljs'

const router = express.Router()

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

// 全局进度状态
let globalProgress = {
    isProcessing: false,
    total: 0,
    finished: 0,
    success: 0,
    failed: 0,
    currentBatch: 0,
    totalBatches: 0,
    message: ''
};

/**
 * 进度查询接口
 * GET /api/douyiin/progress
 */
router.get('/progress', (req, res) => {
    res.json({
        code: 200,
        data: globalProgress
    });
});

/**
 * 重置进度状态
 */
function resetProgress() {
    globalProgress = {
        isProcessing: false,
        total: 0,
        finished: 0,
        success: 0,
        failed: 0,
        currentBatch: 0,
        totalBatches: 0,
        message: ''
    };
}

/**
 * 更新进度状态
 */
function updateProgress(progress) {
    Object.assign(globalProgress, progress);
}

// 性能优化配置
const CONFIG = {
    BATCH_SIZE: 1,              // 每批处理数量，改为1
    DELAY_BETWEEN_BATCHES: 10000, // 批次间延迟(ms)，改为10秒
    DELAY_BETWEEN_REQUESTS: 5000, // 请求间延迟(ms)，每条2秒
    MAX_RETRIES: 3,              // 最大重试次数
    TIMEOUT: 30000,              // 请求超时时间(ms)
    MAX_CONCURRENT: 1            // 最大并发数
}

/**
 * 通用 GET 请求方法（带重试机制）
 */
export function requestGet(url, params, callback, retryCount = 0) {
    let fullUrl = url
    if (params && typeof params === 'object') {
        const query = Object.entries(params)
            .map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`)
            .join('&')
        fullUrl += (url.includes('?') ? '&' : '?') + query
    }
    
    const requestOptions = {
        url: fullUrl,
        timeout: CONFIG.TIMEOUT
    }
    
    request.get(requestOptions, function (error, response, body) {
        if (error && retryCount < CONFIG.MAX_RETRIES) {
            console.log(`请求失败，${CONFIG.DELAY_BETWEEN_REQUESTS/1000}秒后重试 (${retryCount + 1}/${CONFIG.MAX_RETRIES}): ${url}`)
            setTimeout(() => {
                requestGet(url, params, callback, retryCount + 1)
            }, CONFIG.DELAY_BETWEEN_REQUESTS)
            return
        }
        
        // 默认用 utf-8 解码
        let html = body ? body.toString('utf-8') : ''
        // 检查 meta 标签或响应头
        const charsetMatch = html.match(/<meta.*?charset=["']?([\w-]+)["']?/i) ||
            (response && response.headers['content-type'] && response.headers['content-type'].match(/charset=([\w-]+)/i));
        let charset = charsetMatch ? charsetMatch[1].toLowerCase() : 'utf-8'
        if (charset !== 'utf-8' && charset !== 'utf8') {
            try {
                html = iconv.decode(body, charset)
            } catch (e) {
                // fallback
                html = iconv.decode(body, 'gbk')
            }
        }
        callback(error, response, html)
    })
}


/**
 * 批量运行接口
 * POST /api/douyiin/douyinBatchRun
 * body: [{ 视频链接: 'https://www.bilibili.com/video/BV1j4MvzcEnx', ... }, ...]
 */
router.post('/douyinBatchRun', async (req, res) => {
    const data = req.body[0]
    const filePath = data.path
    
    // 立即返回任务ID，开始异步处理
    const taskId = Date.now().toString();
    
    res.json({
        code: 200,
        message: '任务已开始',
        taskId: taskId
    });
    
    // 异步处理数据
    processDouyinData(filePath, taskId);
});

/**
 * 异步处理抖音数据
 */
async function processDouyinData(filePath, taskId) {
    try {
        // 重置进度状态
        resetProgress();
        updateProgress({
            isProcessing: true,
            message: '开始读取文件...'
        });
        
        // 1. 用 fs 读取文件为 Buffer
        const fileBuffer = fs.readFileSync(filePath)

        // 2. 用 xlsx 解析 Buffer
        const workbook = XLSX.read(fileBuffer, {type: 'buffer'})

        // 3. 获取第一个 sheet
        const sheetName = workbook.SheetNames[0]
        const sheet = workbook.Sheets[sheetName]

        // 4. 转为 JSON
        const jsonData = XLSX.utils.sheet_to_json(sheet, {defval: ''})
        
        console.log(`开始处理 ${jsonData.length} 条抖音数据，任务ID: ${taskId}`);
        
        const result = await getBilibiliVideoInfo(jsonData, taskId);
        
        // 处理完成
        updateProgress({
            isProcessing: false,
            message: `处理完成！成功: ${result.success}, 失败: ${result.failed}，文件路径为：${result.message}`,
            finished: result.total,
            success: result.success,
            failed: result.failed
        });
        
    } catch (error) {
        console.error('处理失败:', error);
        updateProgress({
            isProcessing: false,
            message: `处理失败: ${error.message}`
        });
    }
}


function formatTimestamp(ts) {
    const date = new Date(ts * 1000); // 时间戳单位是秒，需乘1000
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, '0');
    const d = String(date.getDate()).padStart(2, '0');
    const h = String(date.getHours()).padStart(2, '0');
    const min = String(date.getMinutes()).padStart(2, '0');
    const s = String(date.getSeconds()).padStart(2, '0');
    return `${y}-${m}-${d} ${h}:${min}:${s}`;
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 专门提取stats格式JSON的函数
function extractStatsJson(html) {
    // 方法1: 直接匹配包含所有stats字段的JSON
    const statsPattern = /\{[^{}]*"commentCount"[^{}]*"diggCount"[^{}]*"shareCount"[^{}]*"playCount"[^{}]*"collectCount"[^{}]*"downloadCount"[^{}]*"forwardCount"[^{}]*"liveWatchCount"[^{}]*"recommendCount"[^{}]*\}/g;


    const matches = html.match(statsPattern);

    if (matches && matches.length > 0) {
        for (const match of matches) {
            try {
                const parsed = JSON.parse(match);
                if (parsed.commentCount !== undefined && parsed.diggCount !== undefined) {
                    return parsed;
                }
            } catch (e) {
                console.log("方法1解析失败:", e.message);
            }
        }
    }

    // 方法2: 查找包含stats字段的JSON对象
    const jsonPattern = /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g;
    const allJsonMatches = html.match(jsonPattern);

    if (allJsonMatches) {
        for (const jsonStr of allJsonMatches) {
            if (jsonStr.includes('"commentCount"') && jsonStr.includes('"diggCount"')) {
                try {
                    const parsed = JSON.parse(jsonStr);
                    if (parsed.commentCount !== undefined && parsed.diggCount !== undefined) {
                        return parsed;
                    }
                } catch (e) {
                    console.log("方法2解析失败:", e.message);
                }
            }
        }
    }

    // 方法3: 使用更宽松的正则匹配
    const loosePattern = /\{[^{}]*"commentCount"[^{}]*\}/g;
    const looseMatches = html.match(loosePattern);

    if (looseMatches) {
        for (const match of looseMatches) {
            try {
                const parsed = JSON.parse(match);
                if (parsed.commentCount !== undefined) {
                    return parsed;
                }
            } catch (e) {
                console.log("方法3解析失败:", e.message);
            }
        }
    }
    return null;
}

// 智能格式化任意时间戳（纳秒/微秒/毫秒/秒）
function formatAnyTimestamp(ts) {
    ts = Number(ts);
    let date;
    if (ts > 1e18) { // 纳秒
        date = new Date(ts / 1e6);
    } else if (ts > 1e15) { // 微秒
        date = new Date(ts / 1e3);
    } else if (ts > 1e12) { // 毫秒
        date = new Date(ts);
    } else { // 秒
        date = new Date(ts * 1000);
    }
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, '0');
    const d = String(date.getDate()).padStart(2, '0');
    const h = String(date.getHours()).padStart(2, '0');
    const min = String(date.getMinutes()).padStart(2, '0');
    const s = String(date.getSeconds()).padStart(2, '0');
    return `${y}-${m}-${d} ${h}:${min}:${s}`;
}

// 提取抖音视频的详细信息
function extractDouyinInfo(html) {
    const result = {
        stats: null,
        uniqueId: null,
        author: null,
        timestamp: null,
        title: null
    };

    // 提取stats数据
    result.stats = extractStatsJson(html);

    // 提取抖音号 (uniqueId)
    const uniqueIdMatch = html.match(/"uniqueId":"([^"]+)"/);
    if (uniqueIdMatch) {
        result.uniqueId = uniqueIdMatch[1];
    }

    // 提取作者信息
    const authorMatch = html.match(/"author":"([^"]+)"/);
    if (authorMatch) {
        result.author = authorMatch[1];
    }

    // 提取时间戳
    const timestampMatch = html.match(/"createTime":(\d+)/);
    if (timestampMatch && timestampMatch[1]) {
        result.timestamp = formatAnyTimestamp(timestampMatch[1]);
        console.log("时间戳:", result.timestamp)
    } else {
        result.timestamp = '未找到';
    }

    // 提取标题
    const titleMatch = html.match(/"shareLinkDesc":"([^"]+)"/);
    if (titleMatch) {
        result.title = titleMatch[1];
    }

    return result;
}

async function getBilibiliVideoInfo(jsonData, taskId) {
    const result = {
        total: jsonData.length,
        success: 0,
        failed: 0,
        taskId: taskId
    };

    console.log(`开始处理 ${result.total} 条数据，分批大小: ${CONFIG.BATCH_SIZE}`);

    const rows = []; // 新增：用于存储每条数据
    // 分批处理
    for (let batchStart = 0; batchStart < result.total; batchStart += CONFIG.BATCH_SIZE) {
        const batchEnd = Math.min(batchStart + CONFIG.BATCH_SIZE, result.total);
        const currentBatch = jsonData.slice(batchStart, batchEnd);
        
        console.log(`处理批次 ${Math.floor(batchStart/CONFIG.BATCH_SIZE) + 1}/${Math.ceil(result.total/CONFIG.BATCH_SIZE)} (${batchStart + 1}-${batchEnd})`);
        
        // 串行处理每条数据
        for (let batchIndex = 0; batchIndex < currentBatch.length; batchIndex++) {
            const idx = batchStart + batchIndex;
            const item = currentBatch[batchIndex];
            const url = item.视频链接;
            await new Promise(resolve => {
                requestGet(url, null, (error, response, body) => {
                    let video = null;
                    let isError = false;
                    
                    if (error) {
                        isError = true;
                        console.log(`请求失败 (${idx + 1}/${result.total}): ${url}`);
                    } else {
                        try {
                            const $ = cheerio.load(body)
                            const html = $.html()
                            const cleanHtml = html.replace(/\s+/g, '').replace(/\\/g, '')
                            const douyinInfo = extractDouyinInfo(cleanHtml);
                            // console.log('html', cleanHtml)

                            if (douyinInfo.stats) {
                                video = {
                                    title: douyinInfo.title || '未找到',
                                    author: douyinInfo.author || '未找到',
                                    uniqueId: douyinInfo.uniqueId || '未找到',
                                    like: douyinInfo.stats.diggCount || 0,
                                    commentCount: douyinInfo.stats.commentCount || 0,
                                    share: douyinInfo.stats.shareCount || 0,
                                    collect: douyinInfo.stats.collectCount || 0,
                                    recommendCount: douyinInfo.stats.recommendCount || 0,
                                    pubdate: douyinInfo.timestamp || '未找到', // 直接用格式化后的字符串
                                    url
                                }
                                console.log(`✅ 成功处理 (${idx + 1}/${result.total}): ${video.title}`);
                            } else {
                                // 备用B站逻辑
                                try {
                                    const data = JSON.parse(body)
                                    video = {
                                        title: $.title || '未找到',
                                        author: data.data?.owner?.name || '未找到',
                                        uniqueId: '未找到',
                                        like: data.data?.stat?.like || 0,
                                        commentCount: data.data?.stat?.reply || 0,
                                        share: data.data?.stat?.share || 0,
                                        collect: data.data?.stat?.favorite || 0,
                                        recommendCount: 0,
                                        pubdate: data.data?.pubdate ? formatTimestamp(data.data.pubdate) : '未找到',
                                        url
                                    }
                                    console.log(`✅ 备用解析成功 (${idx + 1}/${result.total}): ${video.title}`);
                                } catch (e) {
                                    console.log(`❌ 解析失败 (${idx + 1}/${result.total}): ${url}`);
                                    isError = true;
                                    video = {
                                        title: '解析失败',
                                        author: '解析失败',
                                        uniqueId: '解析失败',
                                        playCount: 0,
                                        like: 0,
                                        commentCount: 0,
                                        share: 0,
                                        collect: 0,
                                        recommendCount: 0,
                                        pubdate: '解析失败',
                                        url
                                    }
                                }
                            }
                            
                            // 检查字段完整性
                            for (const key in video) {
                                if (video[key] === undefined || video[key] === null || video[key] === '') {
                                    isError = true;
                                    break;
                                }
                            }
                        } catch (e) {
                            console.log(`❌ 处理异常 (${idx + 1}/${result.total}): ${e.message}`);
                            isError = true;
                        }
                    }
                    
                    if (isError) {
                        result.failed++;
                    } else {
                        result.success++;
                    }
                    // 新增：保存每条数据到rows
                    rows.push([
                        video.title,
                        video.author,
                        video.uniqueId,
                        video.like,
                        video.commentCount,
                        video.share,
                        video.collect,
                        video.recommendCount,
                        video.pubdate,
                        video.url
                    ]);
                    // 实时更新进度
                    updateProgress({
                        total: result.total,
                        finished: result.success + result.failed,
                        success: result.success,
                        failed: result.failed,
                        message: `进度: ${result.success + result.failed}/${result.total}`
                    });
                    resolve();
                });
            });
            // 每条请求后sleep
            await sleep(CONFIG.DELAY_BETWEEN_REQUESTS);
        }
        // 批次间延迟（最后一批不需要延迟）
        if (batchEnd < result.total) {
            console.log(`批次完成，等待 ${CONFIG.DELAY_BETWEEN_BATCHES/1000} 秒后继续...`);
            await sleep(CONFIG.DELAY_BETWEEN_BATCHES);
        }
    }

    console.log(`✅ 所有抖音数据处理完成！成功: ${result.success}, 失败: ${result.failed}`);

    // 保存结果
    const workbook = new ExcelJS.Workbook();
    const worksheet = workbook.addWorksheet('Sheet1');

    // 表头
    const header = ['标题', '作者', '抖音号', '点赞', '评论', '转发', '收藏', '推荐数', '发布日期', '视频链接'];
    worksheet.addRow(header);

    // 确保 uploads/result 目录存在
    const uploadDir = path.resolve(process.cwd(), 'uploads/result')
    if (!fs.existsSync(uploadDir)) {
        fs.mkdirSync(uploadDir, {recursive: true});
    }

    const date = new Date();
    const timestamp = date.toISOString().replace(/[:.]/g, '-').slice(0, 19);
    const savePath = path.join(uploadDir, `${timestamp}_douyin_result.xlsx`);

    // 写入所有数据行
    rows.forEach(row => worksheet.addRow(row));

    await workbook.xlsx.writeFile(savePath);
    console.log('✅ 已保存到 ' + savePath);
    
    return {
        code: 200,
        message: "文件路径为" + savePath,
        total: result.total,
        success: result.success,
        failed: result.failed,
        progress: 100
    }
}

export default router